/****************************************************************
* Filename :    TheTest.c                                       *
* Author :      Vincent Léger & Francisco Ramos                 *
* Description : Test program for TheBuffer                      *
*                A poor non-failsafe terminal application made  *
*                intended to test a char buffer on linux OS     *
* Modif. :                                                      *
*                                                               *
*Note :        Sorry, documentation is poor, but software is    *
*              pretty straight-forward...                       *
****************************************************************/

/****************************************************************
* Includes                                                      *
****************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <time.h>

/****************************************************************
* Defines                                                       *
****************************************************************/
//string limits
#define MAX_POSSIBLE_SIZE   10000
#define MAX_CHAR_FILENAME   50
//device multiple status definition
#define STATUS_OPEN     1
#define STATUS_CLOSED   0
#define ACCESS_NOBLOCK  1
#define ACCESS_BLOCK    2
#define MODE_READWRITE  0
#define MODE_READ       1
#define MODE_WRITE      2

//initial driver name
#define DEFAULT_DRIVER          "/dev/TheBuffer"
//#define DEFAULT_DRIVER	"/dev/etsele_cdev"

//main window user inputs (other windows are hard-coded)
#define INPUT_OPEN      1
#define INPUT_CLOSE     2
#define INPUT_READ      3
#define INPUT_WRITE     4
#define INPUT_IOCTL     5
#define INPUT_RENAME    6
#define INPUT_ACCESS    7
#define INPUT_MODE      8
#define INPUT_QUIT      9

//IOCTL definitions (used to trigger IOCT commands on TheBuffer)
//Specific to this very module (The Buffer)
#define THEBUFFER_IOCTL_MAGIC_NO        'V'
#define THEBUFFER_IOCTL_MAXVALUE        4
#define THEBUFFER_IOCTL_RESET           _IO(THEBUFFER_IOCTL_MAGIC_NO, 0)
#define THEBUFFER_IOCTL_GET_QTY_DATA    _IOR(THEBUFFER_IOCTL_MAGIC_NO, 1, int)   //GetNumData
#define THEBUFFER_IOCTL_GET_QTY_READER  _IOR(THEBUFFER_IOCTL_MAGIC_NO, 2, int)   //GetNumReader
#define THEBUFFER_IOCTL_GET_BUFFERSIZE  _IOR(THEBUFFER_IOCTL_MAGIC_NO, 3, int)   //GetBufSize
#define THEBUFFER_IOCTL_SET_BUFFERSIZE  _IOR(THEBUFFER_IOCTL_MAGIC_NO, 4, int)   //SetBufSize

/****************************************************************
* TYPES + STRUCTURES                                            *
****************************************************************/
//none... code is very ugly anyways...

/****************************************************************
* GLOBALS (yeah right... using globals 'cause I'm lazy...)      *
****************************************************************/
int fileHandle = 0;
char fileName[MAX_CHAR_FILENAME+1] = {DEFAULT_DRIVER};
int fileStatus = STATUS_CLOSED;
int accessType = ACCESS_BLOCK;
int openMode = MODE_READWRITE;

/****************************************************************
* Prototypes                                                    *
****************************************************************/
void waitsec(unsigned int);
void ShowMainScreen(void);
void ShowReadScreen(void);
void ShowWriteScreen(void);
void ShowExitScreen(void);
void ShowIOCTLMain(void);
void ShowModeScreen(void);
void ShowAccessScreen(void);
void ShowRenameScreen(void);
void ShowOpenScreen(void);
void ShowCloseScreen(void);
//depracated (behavior is not always predictable)
//void WaitEnter(void);

/******************************************************************************
 * Main
 *****************************************************************************/
int main(int argc, char **argv)
{
    int userInput;
    short quitFlag;
    quitFlag = 0;

    while (!quitFlag)	//THE MAIN LOOP loops until user quits
    {
        ShowMainScreen();
        printf("Votre choix :");
        userInput = -1;

        scanf("%d", &userInput);
        getchar();  //to clean up input...

        switch(userInput)
        {
            case (INPUT_OPEN):
            {
                ShowOpenScreen();
                break;
            }
            case (INPUT_CLOSE):
            {
                ShowCloseScreen();
                break;
            }
            case (INPUT_READ):
            {
                if (fileStatus == STATUS_OPEN)
                    ShowReadScreen();
                else
                {
                    printf("\nLe driver n'est pas 'ouvert' ... SVP... ouvrir et réessayer...\n");
                    waitsec(3);
                }
                break;
            }
            case (INPUT_WRITE):
            {
                if (fileStatus == STATUS_OPEN)
                    ShowWriteScreen();
                else
                {
                    printf("\nLe driver n'est pas 'ouvert' ... SVP... ouvrir et réessayer...\n");
                    waitsec(2);
                }
                break;
            }
            case (INPUT_IOCTL):
            {
                if (fileStatus == STATUS_OPEN)
                    ShowIOCTLMain();
                else
                {
                    printf("\nLe driver n'est pas 'ouvert' ... SVP... ouvrir et réessayer...\n");
                    waitsec(2);
                }   
                break;
            }
            case (INPUT_RENAME):
            {
                if (fileStatus != STATUS_OPEN)
                    ShowRenameScreen();
                else
                {
                    printf("\nSVP, veuillez fermer le fichier avant de changer le nom\n");
                    waitsec(2);
                }
                break;
            }
            case (INPUT_ACCESS):
            {
                if (fileStatus != STATUS_OPEN)
                    ShowAccessScreen();
                else
                {
                    printf("\nSVP, veuillez fermer le fichier avant de changer le type d'accès\n");
                    waitsec(2);
                }
                break;
            }
            case (INPUT_MODE):
            {
                if (fileStatus != STATUS_OPEN)
                    ShowModeScreen();
                else
                {
                    printf("\nSVP, veuillez fermer le fichier avant de changer le mode\n");
                    waitsec(2);
                }
                break;
            }
            case (INPUT_QUIT):
            {
                ShowExitScreen();
                quitFlag = 1;
                break;
            }
            default:
            {
                printf("\nENTRÉE INVALIDE !!!");
                waitsec(2);
                break;
            }
        }
    }
    return 0;

}

/******************************************************************************
* waitsec() function made by Maxim Circé (Thanks dude)                        *
* This function basically waits for given number of seconds on linux OS       *
******************************************************************************/
void waitsec(unsigned int sec){

        clock_t endwait;

        endwait = clock () + sec*CLOCKS_PER_SEC ;
        while (clock() < endwait) {}

}

/******************************************************************************
* Show[something] functions                                                   *
* Really basic hard-coded functions for multiple display screens              *
* note: These function self-handle user inputs in there own-generated menus   *
******************************************************************************/

void ShowMainScreen(void)
{
    system("clear");
    printf("*****         The TEST          *****\n");
    printf("Cette application permet de tester The Buffer.\n\n");
    printf("Par Vincent Léger et Francisco Ramos\n");
    printf("******************************************\n");
    printf("******************Status******************\n");
    printf("Fichier : %s\n",fileName);
    if (fileStatus == STATUS_OPEN)
        printf("État : Ouvert\n");
    else
        printf("État : Fermé\n");
    if (accessType == ACCESS_NOBLOCK)
        printf("Type d'accès : Non-Bloquant\n");
    else
        printf("Type d'accès : Bloquant\n");
    if (openMode == MODE_READWRITE)
        printf("Mode d'ouverture : LECTURE + ÉCRITURE\n");
    else if (openMode == MODE_READ)
        printf("Mode d'ouverture : LECTURE seule\n");
    else if (openMode == MODE_WRITE)
        printf("Mode d'ouverture : ÉCRITURE seule\n");
    else
        printf("Mode d'ouverture : ----\n");
    printf("******************************************\n");
    printf("Choisissez une option :\n");
    printf("1)Ouvrir le pilote (fichier)\n");
    printf("2)Fermer le pilote (fichier)\n");
    printf("3)Lecture\n");
    printf("4)Écriture\n");
    printf("5)Commandes IOCTL\n");
    printf("6)Changer le nom du pilote (fichier)\n");
    printf("7)Changer type d'accès\n");
    printf("8)Changer mode ouverture\n");
    printf("9)Quitter \n");
    //printf("Votre choix :");
}

void ShowReadScreen(void)
{
    unsigned int userInput;
    unsigned int i;
    int result;

    char dataFromBuffer[MAX_POSSIBLE_SIZE+1];

    for(i=0;i<=MAX_POSSIBLE_SIZE;i++)
        dataFromBuffer[i] = 0;

    userInput = 0;

    system("clear");
    printf("*****        Test de lecture        *****\n\n\n");
    //check if opened and if in read mode
    printf("Entrez le nombre de caractères à lire :\n");
    printf("**Attention, cette application ne peut gérer plus de %d caractères\n", MAX_POSSIBLE_SIZE);
    printf("\nVotre valeur :");
    scanf("%d", &userInput);


    if (userInput > MAX_POSSIBLE_SIZE)
    {
        printf("\nDésolé, ce programme de test ne peut gérer plus de %d caractères...\n", MAX_POSSIBLE_SIZE);
        printf("Le nombre de caractère à lire sera donc de %d.\n", MAX_POSSIBLE_SIZE);
        userInput = MAX_POSSIBLE_SIZE;
    }

    printf("\nVotre valeur : %d\n", userInput);
    waitsec(2);
    result = read(fileHandle, dataFromBuffer, userInput);

    printf("\nTexte lu :\n--> %s\n", dataFromBuffer);

    printf("\nCode de retour %d", result);

    printf("\n Cette page se fermera dans 4 secondes...\n");
    waitsec(4);
}

void ShowWriteScreen(void)
{
    char dataFromUser[MAX_POSSIBLE_SIZE+1] = {""};
    ssize_t result;
    int i;
    unsigned int index;

    for (i=0; i<=MAX_POSSIBLE_SIZE; i++)
        dataFromUser[i] = 0;

    result = 0;

    system("clear");
    printf("*****        Test d'écriture       *****\n\n\n");
    //check if opened and if in read mode
    printf("Entrez le texte à écrire :\n");
    printf("**Attention, cette application ne peut gérer plus de %d caractères\n", MAX_POSSIBLE_SIZE);
    printf("\nVotre valeur -->: ");
    scanf("%s",dataFromUser);


    index = MAX_POSSIBLE_SIZE;
    for (i=MAX_POSSIBLE_SIZE;i>=0;i--)
    {
        if (dataFromUser[i] == 0)
            index = i;  //this will find the last '\0' character ...(END OF STRING)
    }

    printf("\n Écriture en cours...(%d caractères)\n", index);
    waitsec(2);

    result = write(fileHandle, dataFromUser, index);

    printf("\nCode de retour %d\n", (int)result);

    printf("\nCette page se fermera dans 4 secondes...\n");
    waitsec(4);
}

void ShowExitScreen(void)
{
    system("clear");
    printf("*****         The TEST          *****\n\n\n\n");
    printf("FERMETURE EN COURS...\n");

    if (fileHandle > 0)
    {
        printf("\nVous avez oublié de fermer le fichier... Fermeture en cours...\n");
	close(fileHandle);
    }
    waitsec(3);
    system("clear");
}

void ShowAccessScreen(void)
{
    int userInput;
    userInput = -1;

    system("clear");
    printf("*****  Changement du mode d'accès  *****\n\n\n");
    printf("\n Choisir le mode d'accès : \n");
    printf("1)Mode bloquant\n");
    printf("2)Mode non-bloquant\n");
    printf("Votre choix : ");
    scanf("%d", &userInput);
    if (userInput == 1)
    {
        printf("\nMode bloquant sélectionné\n");
        accessType = ACCESS_BLOCK;
    }
    else if (userInput == 2)
    {
        printf("\nMode non-bloquant sélectionné\n");
        accessType = ACCESS_NOBLOCK;
    }
    else
        printf("\nChoix non valide, le mode actuel sera conservé...\n");
    waitsec(2);
}

void ShowModeScreen(void)
{
    int userInput;
    system("clear");
    printf("*****   Changement du mode d'ouverture du fichier  *****\n\n\n");
    printf("Veuillez choisir une des options suivantes :\n");
    printf("1) ÉCRITURE + LECTURE\n");
    printf("2) ÉCRITURE SEULEMENT\n");
    printf("3) LECTURE SEULEMENT\n");
    printf("Votre choix : ");
    userInput = -1;
    scanf("%d", &userInput);
    if (userInput == 1)
    {
        printf("\nChoix final : Mode ÉCRITURE + LECTURE");
        openMode = MODE_READWRITE;
    }
    else if (userInput == 2)
    {
        printf("\nChoix final : Mode ÉCRITURE seule");
        openMode = MODE_WRITE;
    }
    else if (userInput == 3)
    {
        printf("\nChoix final : Mode LECTURE seule");
        openMode = MODE_READ;
    }
    else
        printf("\nChoix non valide... La valeur actuelle sera conservée.\n");
    waitsec(2);
}

void ShowRenameScreen(void)
{
    int i;
    int index;
    char newFileName[MAX_CHAR_FILENAME+1] = {""};

    system("clear");
    printf("*****         Nom du fichier       *****\n\n\n");
    printf("Veuillez entrer le nom complet du pilote à utiliser :\n");
    printf("(maximum 50 caractères)\n");
    printf("Votre choix : ");
    scanf("%s", newFileName);


    index = -1;
    for (i=MAX_CHAR_FILENAME;i>=0;i--)
    {
        if (newFileName[i] == 0)
            index = 1;
    }
    for (i=0;i<=MAX_CHAR_FILENAME;i++)
        fileName[i] = 0;

    if ((index > MAX_CHAR_FILENAME)||(index > 0))
    {
        printf("\nNom invalide, le nom de fichier actuel sera conservé...\n");
    }
    else
    {
        printf("\nNom de pilote changé avec succès.\n");
        sprintf(fileName,newFileName,index);
    }

    waitsec(3);

}

void ShowIOCTLMain(void)
{
    int userInput;
    int errCode;
    long OutputValue;

    errCode = 0;
    OutputValue = -1;
    userInput = -1;

    system("clear");
    printf("*****         Commandes IOCTL       *****\n\n\n");
    printf("Veuillez choisir l'une des commandes suivantes :\n");
    printf("1) ResetBuffer\n");
    printf("2) GetNumData\n");
    printf("3) GetNumReader\n");
    printf("4) GetBufSize\n");
    printf("5) SetBufSize\n");
    printf("Votre choix : ");

    scanf("%d", &userInput);

    switch(userInput)
        {
        case (1):
            {
                printf("\nRéinitialisation de TheBuffer...\n");
                errCode = ioctl(fileHandle, THEBUFFER_IOCTL_RESET, &OutputValue);
                if (errCode >= 0)
                {
                    printf("\nRéinitialisation termine\n");
                }
                else
                {
                    printf("\nErreur dans l'initialisation. Code = %d\n",(int)errCode);
                }
                break;
            }
        case (2):
            {
                printf("\nGetNumData\n");
                errCode = ioctl(fileHandle, THEBUFFER_IOCTL_GET_QTY_DATA, &OutputValue);
                if (errCode >= 0)
                {
                    printf("\nLe nombre de caractères dans le TheBuffer est : %d\n", (int)OutputValue);
                }
                else
                {
                    printf("\nErreur ! Code = %d\n",(int)errCode);
                }
                break;
            }
        case (3):
            {
                printf("\nGetNumReader\n");
                errCode = ioctl(fileHandle, THEBUFFER_IOCTL_GET_QTY_READER , &OutputValue);
                if (errCode >= 0)
                {
                    printf("\nLe nombre actuel de lecteur est : %d\n", (int)OutputValue);
                }
                else
                {
                    printf("\nErreur ! Code = %d\n",(int)errCode);
                }
                break;
            }
        case (4):
            {
                printf("\nGetBufSize\n");
                errCode = ioctl(fileHandle, THEBUFFER_IOCTL_GET_BUFFERSIZE, &OutputValue);
                if (OutputValue >= 0)
                {
                    printf("\nLa taille maximale actuelle du tampon est de %d caractères\n", (int)OutputValue);
                }
                else
                {
                    printf("\nErreur ! Code = %d\n",(int)errCode);
                }
                break;
            }
        case (5):
            {
                errCode = ioctl(fileHandle, THEBUFFER_IOCTL_GET_BUFFERSIZE, &OutputValue);
                if (errCode >= 0)
                {
                    printf("\nLa taille maximale actuelle du tampon est de %d caractères\n", (int)OutputValue);
                    printf("\nSetBufSize\n");
                    printf("\nSélectionnez la nouvelle taille du tampon :");
                    printf("\nValeur : ");
                    scanf("%d", &userInput);
                    printf("\nChangement en cours...");
                    errCode = ioctl(fileHandle, THEBUFFER_IOCTL_SET_BUFFERSIZE, &userInput);
                    if (errCode >= 0)
                    {
                        waitsec(1);
                        errCode = ioctl(fileHandle, THEBUFFER_IOCTL_GET_BUFFERSIZE, &OutputValue);
                        if (errCode >= 0)
                        {
                            printf("\nLa taille maximale actuelle du tampon est de %d caractères\n",(int)OutputValue);
                        }
                        else
                        {
                            printf("\nErreur ! Code = %d\n",(int)errCode);
                        }
                    }
                    else
                    {
                        printf("\nErreur ! Code = %d\n",(int)errCode);
                    }

                }
                else
                {
                    printf("\nErreur ! Code = %d\n",(int)errCode);
                }
                break;
            }
        default:
            {
                printf("\nCommande non-valide...\n");
                break;
            }
        }


    waitsec(3);
}

void ShowOpenScreen(void)
{
    int fflags;
    fflags = 0;

    if (accessType == ACCESS_NOBLOCK)
        fflags = O_NONBLOCK;

    ShowMainScreen();
    if (fileHandle>0)
        printf("\nFICHIER DÉJÀ OUVERT !!!\n");
    else
    {
        printf("\nOuverture en cours...\n");
        waitsec(1);
        fileStatus = STATUS_OPEN;
        if (openMode == MODE_READWRITE)
        {
            fileHandle = open(fileName, O_RDWR | fflags);
        }
        else if (openMode == MODE_WRITE)
        {
            fileHandle = open(fileName, O_WRONLY | fflags);
        }
        else if (openMode == MODE_READ)
        {
            fileHandle = open(fileName, O_RDONLY | fflags);
            //open(fileName, O_RDONLY,)
        }
        else
        {
            printf("\nMode de lecture inconnu !\n");
        }

        if(fileHandle < 0)
        {
            fileStatus = STATUS_CLOSED;
            printf("\nERREUR EN OUVERTURE... VÉRIFIEZ LES DROITS D'ACCÈS..\n");
        }
        else
        {
            printf("\nFichier ouvert avec succès !\n");
        }
    }
    waitsec(3);
}

void ShowCloseScreen(void)
{
    ShowMainScreen();
    printf("\nFermeture...\n");
    waitsec(1);
    if (fileHandle<=0)
    {
        printf("\nAucun fichier ouvert...\n");
    }
    else
    {
        close(fileHandle);
        fileHandle = 0;
        fileStatus = STATUS_CLOSED;
        printf("\nFichier fermé avec succès\n");
    }
    waitsec(3);
}

/*void WaitEnter(void)
{
    char dummyValue;
    dummyValue = 0;
    printf("\nAppuyez sur ENTER pour continuer...\n");
    scanf("%c", &dummyValue);
}*/

